package spatialanalyst;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import org.osoa.sca.annotations.Service;

import calculator.AddService;

import com.esri.arcgis.datasourcesfile.ShapefileWorkspaceFactory;
import com.esri.arcgis.display.CancelTracker;
import com.esri.arcgis.geodatabase.DENetworkDataset;
import com.esri.arcgis.geodatabase.FeatureCursor;
import com.esri.arcgis.geodatabase.GPMessages;
import com.esri.arcgis.geodatabase.IDataset;
import com.esri.arcgis.geodatabase.IFeatureClass;
import com.esri.arcgis.geodatabase.NetworkDataset;
import com.esri.arcgis.geodatabase.NetworkDatasetWorkspaceExtension;
import com.esri.arcgis.geodatabase.QueryFilter;
import com.esri.arcgis.geodatabase.Workspace;
import com.esri.arcgis.geodatabase.esriDatasetType;
import com.esri.arcgis.geoprocessing.GeoProcessor;
import com.esri.arcgis.geoprocessing.tools.datamanagementtools.SaveToLayerFile;
import com.esri.arcgis.networkanalyst.INAClass;
import com.esri.arcgis.networkanalyst.INAClassFieldMap;
import com.esri.arcgis.networkanalyst.INAClassLoader;
import com.esri.arcgis.networkanalyst.INAContext;
import com.esri.arcgis.networkanalyst.INALayer;
import com.esri.arcgis.networkanalyst.INASolver;
import com.esri.arcgis.networkanalyst.NAClassFieldMap;
import com.esri.arcgis.networkanalyst.NAClassLoader;
import com.esri.arcgis.networkanalyst.NAContext;
import com.esri.arcgis.networkanalyst.NALayer;
import com.esri.arcgis.networkanalyst.NARouteSolver;
import com.esri.arcgis.networkanalyst.esriNAOutputLineType;
import com.esri.arcgis.system.AoInitialize;
import com.esri.arcgis.system.EngineInitializer;
import com.esri.arcgis.system.IStringArray;
import com.esri.arcgis.system.ITrackCancel;
import com.esri.arcgis.system.UID;
import com.esri.arcgis.system.esriLicenseExtensionCode;
import com.esri.arcgis.system.esriLicenseProductCode;
import com.esri.arcgis.system.esriLicenseStatus;

@Service(RouteService.class)
public class RouteServiceImpl implements RouteService {

    public INALayer SolveRoute(String shapeWorkspacePath, String inputStopsFC,
            String shapeInputNameField, String networkDatasetName,
            String outLayerPath) {
        // TODO Auto-generated method stub

        // Open the feature workspace, input feature class, and network dataset
        try {
            ShapefileWorkspaceFactory workspaceFactory = new ShapefileWorkspaceFactory();

            Workspace workspace = (Workspace) workspaceFactory.openFromFile(
                    shapeWorkspacePath, 0);
            IFeatureClass inputStopsFClass = workspace.openFeatureClass(inputStopsFC);

            UID uid = new UID();
            uid.setValue("esriGeoDatabase.NetworkDatasetWorkspaceExtension");
            NetworkDatasetWorkspaceExtension networkDatasetWorkspaceExtension = new NetworkDatasetWorkspaceExtension(
                    workspace.findExtension(uid));
            IDataset dataset = networkDatasetWorkspaceExtension.getDatasetByName(esriDatasetType.esriDTNetworkDataset,
                    networkDatasetName);
            NetworkDataset networkDataset = new NetworkDataset(dataset);

            // Create the Route NALayer
            INALayer naLayer = createRouteAnalysisLayer("Route", networkDataset);
            INAContext naContext = naLayer.getContext();
            INAClass stopsNAClass = (INAClass) naContext.getNAClasses().getItemByName("Stops");

            // Load the Stops
            INAClassFieldMap naClassFieldMap = new NAClassFieldMap();
            naClassFieldMap.setMappedField("Name", shapeInputNameField);

            INAClassLoader naLoader = new NAClassLoader();
            naLoader.setLocatorByRef(naContext.getLocator());
            naLoader.setNAClassByRef(stopsNAClass);
            naLoader.setFieldMapByRef(naClassFieldMap);

            int[] rowsInCursor = {0};
            int[] rowsLocated = {0};
            ITrackCancel cancelTracker = new CancelTracker();
            FeatureCursor cursor = new FeatureCursor(inputStopsFClass.search(
                    new QueryFilter(), false));
            naLoader.load(cursor, cancelTracker, rowsInCursor, rowsLocated);

            // Solve
            INASolver naSolver = naContext.getSolver();
            naSolver.solve(naContext, new GPMessages(), cancelTracker);

            // Save the layer to disk
            SaveLayerToDisk(naLayer, outLayerPath);
            return naLayer;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    private INALayer createRouteAnalysisLayer(String layerName,
            NetworkDataset networkDataset) {
        NALayer naLayer = null;
        try {
            NARouteSolver naRouteSolver = new NARouteSolver();

            // Get the NetworkDataset's Data Element
            DENetworkDataset deNetworkDataset = (DENetworkDataset) networkDataset.getDataElement();

            // Create the NAContext and bind to it
            NAContext naContext = (NAContext) naRouteSolver.createContext(
                    deNetworkDataset, layerName);
            naContext.bind(networkDataset, new GPMessages());

            // Create the NALayer
            naLayer = (NALayer) naRouteSolver.createLayer(naContext);
            naLayer.setName(layerName);

            // Set some properties on the the route solver interface
            naRouteSolver.setFindBestSequence(true);
            naRouteSolver.setPreserveFirstStop(true);
            naRouteSolver.setPreserveLastStop(false);
            naRouteSolver.setUseTimeWindows(false);
            naRouteSolver.setOutputLines(esriNAOutputLineType.esriNAOutputLineTrueShapeWithMeasure);

            // Set some properties on the general INASolverSettings interface
            IStringArray restrictions = naRouteSolver.getRestrictionAttributeNames();
            restrictions.add("Oneway");
            naRouteSolver.setRestrictionAttributeNamesByRef(restrictions);

            // Update the context based on the changes made to the solver
            // settings
            naRouteSolver.updateContext(naContext, deNetworkDataset,
                    new GPMessages());
        } catch (Exception e) {
            e.printStackTrace();
        }
        // Return the layer...
        return naLayer;
    }

    private void SaveLayerToDisk(INALayer inLayer, String outLayer) {
        // Here, use GeoProcessing to save the layer to disk...
        try {
            GeoProcessor gp = new GeoProcessor();
            gp.setOverwriteOutput(true);

            // Never use the default constructor for a GP tool....
            SaveToLayerFile sTool = new SaveToLayerFile(inLayer, outLayer);

            // Execute the tool...
            gp.execute(sTool, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void initializeArcGISLicenses(AoInitialize initializer) {
        try {
            if (initializer.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeEngine) == esriLicenseStatus.esriLicenseAvailable) {
                initializer.initialize(esriLicenseProductCode.esriLicenseProductCodeEngine);
            }
            initializer.checkOutExtension(esriLicenseExtensionCode.esriLicenseExtensionCodeNetwork);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String runRoute() {
        System.out.println("Starting Route Layer - An ArcObjects Java SDK Developer Sample");
        String arcGISHome = null;
        try {
            arcGISHome = System.getenv("ARCGISHOME");
        } catch (Error e) {
            BufferedReader console = new BufferedReader(new InputStreamReader(
                    System.in));
            System.out.print("Please enter the path to your ArcGIS installation directory: ");
            try {
                arcGISHome = console.readLine();
            } catch (IOException e1) {
                e1.printStackTrace();
            } // This should never happen

        }
        if (!(new File(arcGISHome).exists())) {
            System.out.println(arcGISHome + " does not exist.\nExiting...");
            System.exit(-1);
        }

        String shapeWorkspacePath = arcGISHome + "/java/samples/data/networkanalyst";
        String inputStopsFC = "MultiRouteStopsWithTW";
        String shapeInputNameField = "Name";
        String networkDatasetName = "streets_nd";

        String outDataPath = arcGISHome + "/java/samples/output_data/routelayer";
        String outLayerPath = outDataPath + "/routelayer.lyr";

        File outData = new File(outDataPath);
        outData.mkdirs();

        EngineInitializer.initializeEngine();
        try {
            AoInitialize aoInitializer = new AoInitialize();
            aoInitializer.initialize(esriLicenseProductCode.esriLicenseProductCodeEngine);
            initializeArcGISLicenses(aoInitializer);

            SolveRoute(shapeWorkspacePath, inputStopsFC, shapeInputNameField,
                    networkDatasetName, outLayerPath);

            System.out.println("Done.\nGenerated layer in " + outData.getAbsolutePath());
            System.out.println("Add it to a map to see the results.");
            aoInitializer.shutdown();
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
            System.exit(-1);
        }
        return "Succeffult route";
    }
}
